#include "stdafx.h"
#include "Terrain.h"
#include "Constant.h"
#include "Globals.h"
#include "NFG.h"
#include "ImageTGA.h"

using namespace TerrainDefine;

Terrain::Terrain()
{
	m_maLocalW.SetIdentity();
}

void Terrain::LoadTexture2D(char **fileName, int iCount)
{
}

void Terrain::GenVertex(int iNumVertexRows, int iNumVertexCols, int iNumCellRows, int iNumCellCols, float fDx, float fDz)
{
	//Setup size of terrain
	float fWidth = iNumCellCols * fDx;
	float fHeight = iNumCellRows * fDz;

	float fOffsetX = -fWidth / 2.0f;
	float fOffsetZ = -fHeight / 2.0f;

	m_fWidth = fWidth;
	m_fHeight = fHeight;

	///Calculate vertex position

	//genvertex
	m_Model->m_uiNumVertex = iNumVertexRows * iNumVertexCols;
	m_Model->m_aVertexData = new Vertex[m_Model->m_uiNumVertex];
	int idx = 0;
	for(int i=0; i<iNumVertexRows; i++)
	{
		for(int j=0; j<iNumVertexCols; j++)
		{
			m_Model->m_aVertexData[idx].Pos.x = j*fDx + fOffsetX;
			m_Model->m_aVertexData[idx].Pos.y = m_HeightMapData.HeightData[idx];
			//m_aVertexData[idx].Pos.y = 0.0f;
			m_Model->m_aVertexData[idx].Pos.z = i*fDz + fOffsetZ;

			m_Model->m_aVertexData[idx].TexCood.x = (m_Model->m_aVertexData[idx].Pos.x - fOffsetX)/ fWidth * TERRAIN_SCALE_XZ;
			m_Model->m_aVertexData[idx].TexCood.y = (m_Model->m_aVertexData[idx].Pos.z - fOffsetZ) / fWidth * TERRAIN_SCALE_XZ;
			idx++;
		}
	}
}

void Terrain::GenIndices(int iNumVertexRows, int iNumVertexCols, int iNumCellRows, int iNumCellCols)
{
	int iNumTriagnle = iNumCellRows * iNumCellCols * 2;
	m_Model->m_uiNumIndice = iNumTriagnle * 3;
	m_Model->m_aIndiceData = new GLuint[m_Model->m_uiNumIndice];
	int idx = 0;
	for(int i=0; i<iNumCellRows; ++i)
	{
		for(int j=0; j<iNumCellRows; ++j)
		{
			//First triangle of cell; ABC
			m_Model->m_aIndiceData[idx++] = i*iNumVertexCols + j;		//A
			m_Model->m_aIndiceData[idx++] = i*iNumVertexCols + (j + 1);//B
			m_Model->m_aIndiceData[idx++] = (i + 1)*iNumVertexCols + j;//C
			GenNormVector(m_Model->m_aIndiceData[idx - 3],
						m_Model->m_aIndiceData[idx - 2],
						m_Model->m_aIndiceData[idx - 1]);

			//Second triangle of cell; CBD
			m_Model->m_aIndiceData[idx++] = (i + 1)*iNumVertexCols + j;	//C
			m_Model->m_aIndiceData[idx++] = i*iNumVertexCols + (j + 1);	//B
			m_Model->m_aIndiceData[idx++] = (i + 1)*iNumVertexCols + (j + 1);//D
			GenNormVector(m_Model->m_aIndiceData[idx - 3],
						m_Model->m_aIndiceData[idx - 2],
						m_Model->m_aIndiceData[idx - 1]);
		}
	}

	for(int i=0; i<iNumVertexRows; i++)
	{
		for(int j=0; j<iNumVertexCols; j++)
		{
			m_Model->m_aVertexData[j*iNumVertexCols + j].Norm = (m_Model->m_aVertexData[j*iNumVertexCols + j].Norm / 6).Normalize();
		}
	}
}

void Terrain::GenNormVector(int AID, int BID, int CID)
{
	Vector3 A = m_Model->m_aVertexData[AID].Pos;
	Vector3 B = m_Model->m_aVertexData[BID].Pos;
	Vector3 C = m_Model->m_aVertexData[CID].Pos;

	Vector3 AB = B - A;
	Vector3 AC = B - A;
	Vector3 BA = A - B;
	Vector3 BC = C - A;
	Vector3 CA = A - C;
	Vector3 CB = B - C;

	m_Model->m_aVertexData[AID].Norm += AB.Cross(AC);
	m_Model->m_aVertexData[BID].Norm += BA.Cross(BC);
	m_Model->m_aVertexData[CID].Norm += CA.Cross(CB);
}

void Terrain::Init(char *fileHeightMapSrc)
{
	//Load Texture Data
	m_uiTexCount = 6;
	char **texFileNames = new char*[m_uiTexCount];
	texFileNames[0] = "../Resources/Textures/Blend.tga";
	texFileNames[1] = "../Resources/Textures/Grass.tga";
	texFileNames[2] = "../Resources/Textures/Dirt.tga";
	texFileNames[3] = "../Resources/Textures/Rock.tga";
	texFileNames[4] = "../Resources/Textures/Water.tga";
	texFileNames[5] = "../Resources/Textures/Road.tga";
	m_Texture = new cObjectTexture();
	m_Texture->InitTexture2D(texFileNames, m_uiTexCount);
	SAFE_DEL_ARRAY(texFileNames);

	//Load Vertex Data
	float fDx, fDz;

	int iNumVertexRows = MAP_HEIGHT;
	int iNumVertexCols = MAP_WIDTH;
	int iNumCellRows = iNumVertexRows - 1;
	int iNumCellCols = iNumVertexCols - 1;
	m_fDx = 1.0f;
	m_fDz = 1.0f;

	//Load Height Map data
	m_HeightMapData = HeightField();
	m_HeightMapData.LoadHeightData(fileHeightMapSrc, iNumVertexRows, iNumVertexCols, 1.0f);

	m_Model = new cObjectModel();
	GenVertex(iNumVertexRows, iNumVertexCols, iNumCellRows, iNumCellCols, 1.0f, 1.0f);
	GenIndices(iNumVertexRows, iNumVertexCols, iNumCellRows, iNumCellCols);

	glGenBuffers(1, &m_Model->m_uiVertexID);
	glBindBuffer(GL_ARRAY_BUFFER, m_Model->m_uiVertexID);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * m_Model->GetVertexNumber(), m_Model->m_aVertexData, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glGenBuffers(1, &m_Model->m_uiIndiceID);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Model->m_uiIndiceID);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_Model->GetIndiceNumber(), m_Model->m_aIndiceData, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	m_Shader = Shaders();
	m_Shader.Init("../Resources/Shaders/TriangleShaderVS.vs", "../Resources/Shaders/TriangleShaderFS.fs");
}

void Terrain::Draw(ESContext *esContext)
{
	glUseProgram(m_Shader.program);

	glBindBuffer(GL_ARRAY_BUFFER, m_Model->GetVertexID());
	int size;
	glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
	size /= sizeof(Vertex);
	if(m_Shader.VertPosition != -1)
	{
		glEnableVertexAttribArray(m_Shader.VertPosition);
		glVertexAttribPointer(m_Shader.VertPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	}
	if(m_Shader.VertTexCood != -1)
	{
		glEnableVertexAttribArray(m_Shader.VertTexCood);
		glVertexAttribPointer(m_Shader.VertTexCood, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (char*)0 + sizeof(Vector3));
	}
	if(m_Shader.VertNorm != -1)
	{
		glEnableVertexAttribArray(m_Shader.VertNorm);
		glVertexAttribPointer(m_Shader.VertNorm, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (char*)0 + sizeof(Vector3) + sizeof(Vector2));
	}

	for(int i=0; i<m_uiTexCount; i++)
	{
		if(m_Shader.Texture[i] != -1)
		{
			glActiveTexture(GL_TEXTURE0 + i);
			GLuint texID = m_Texture->GetTextureID()[i];
			glBindTexture(GL_TEXTURE_2D, texID);			
			glUniform1i(m_Shader.Texture[i], i);
			//glBindTexture(GL_TEXTURE_2D, 0);
		}
	}

	if(m_Shader.WVPMatrix != -1)
	{
		Matrix maView = Globals::MainCamera->GetVPMatrix();
		glUniformMatrix4fv(m_Shader.WVPMatrix, 1, GL_FALSE, (GLfloat*)&(m_maLocalW*maView).m[0][0]);
	}
	if(m_Shader.WMatrix != -1)
	{
		glUniformMatrix4fv(m_Shader.WMatrix, 1, GL_FALSE, (GLfloat*)&m_maLocalW.m[0][0]);
	}

	if(m_Shader.CameraPosition != -1)
	{
		Vector3 cameraPosition = Globals::MainCamera->GetPosition();
		glUniform3f(m_Shader.CameraPosition, cameraPosition.x, cameraPosition.y, cameraPosition.z);
	}

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Model->GetIndiceID());
	glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
	size /= sizeof(GLuint);
	glDrawElements(GL_TRIANGLES, m_Model->GetIndiceNumber(), GL_UNSIGNED_INT, 0);
	//glDrawElements(GL_LINE_STRIP, m_Model->GetIndiceNumber(), GL_UNSIGNED_INT, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

Terrain::~Terrain()
{
	SAFE_DEL(m_Texture);
}

float Terrain::GetHeight(float x, float z)
{
	//Transform terrain from local space to "cell" space
	float c = (x + 0.5f*m_fWidth) / m_fDx;
	float r = (z + 0.5f*m_fHeight) / m_fDz;

	//Get row and column here
	int col = (int)c;
	int row = (int)r;

	//Get grid height cell here
	float A = m_HeightMapData.getHeight(row, col);
	float B = m_HeightMapData.getHeight(row, col+1);
	float C = m_HeightMapData.getHeight(row+1, col);
	float D = m_HeightMapData.getHeight(row+1, col+1);

	float s = c - (float)col;
	float t = r - (float)row;

	if (t = 1.0f - s) //Uper triangle ABC
	{
		float uy = B - A;
		float vy = C - A;
		float height = A + s*uy + t*vy;
		esLogMessage("ABC : %f:%f %d:%d = %f\n", x, z, col, row, height);
		return height;
	}
	else //lower triangle DCB
	{
		float uy = C - D;
		float vy = B - D;
		float height = D + (1.0f - s)*uy + (1.0f - t)*vy;
		esLogMessage("DCB : %f:%f %d:%d = %f\n", x, z , col, row, height);
		return height;
	}
}